#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>

// 生产者&消费者模型
// 用于模拟多个线程之间的同步与协作
// 模型中，生产者线程负责生产数据，消费者线程负责消费数据
// 数据通过链表的形式存储在共享的容器中
// 为了确保线程之间的协调和同步，代码使用了互斥锁和条件变量。
using namespace std;

// 链表的节点结构（存储数据）
struct Node
{
    // 数据域
    int val;
    // 指针域
    struct Node *next;
};

// 共享数据的容器
// 指向链表头部的指针，所有的共享数据都存储在这个链表中，供生产者生产数据，消费者消费数据
Node *g_header = NULL;

// 互斥锁（有共享数据就会有冲突，所以引入锁）
// 用于确保多个线程在操作共享数据（链表）时不发生数据竞争（即同一时刻只有一个线程可以修改链表）
pthread_mutex_t g_mutex;

// 条件变量
// 条件变量（Condition Variable）是一种线程同步机制
// 用于协调多个线程之间的通信，通常用于实现 线程等待某个条件满足 的情形。
// 与互斥锁配合使用，条件变量允许线程在某些条件不满足时，进入等待状态，避免忙等（不断轮询消耗CPU）
// 当条件满足时，另一个线程可以发出信号唤醒这些等待的线程。
pthread_cond_t g_cond;

// 生产者
// 生产者线程在 while(1) 中持续生产数据
// 每次循环，生产者创建一个新的链表节点 newNode，节点包含生产的数据 cnt（从 1 开始递增）
// 生产者在向链表插入节点之前，先对全局链表加锁 pthread_mutex_lock(&g_mutex)，确保线程安全
// 完成链表的插入操作后，解锁 pthread_mutex_unlock(&g_mutex)
// 生产者生产完数据后调用 pthread_cond_signal(&g_cond)，通知等待的消费者有新数据可用
// 每次生产一个数据后，生产者线程随机休眠 0 到 2 秒，模拟生产时间的不确定性
void *thread_producer(void *arg)
{
    // 线程分离
    // 表示线程在结束时会自动释放资源，不需要 pthread_join 手动回收
    pthread_detach(pthread_self());

    int cnt = 1;
    while (1)
    {
        // 创建一个链表的节点
        Node *newNode = new Node;
        newNode->val = cnt;
        newNode->next = NULL;

        // 使用互斥锁保护共享数据
        pthread_mutex_lock(&g_mutex);

        // 头插
        newNode->next = g_header;
        g_header = newNode;

        printf("producer %lu, dataId = %d\n", pthread_self(), cnt);
        pthread_mutex_unlock(&g_mutex);

        cnt++;
        pthread_mutex_unlock(&g_mutex);

        // 发条件变量
        pthread_cond_signal(&g_cond);

        sleep(rand() % 3);
    }

    // 线程退出
    pthread_exit(NULL);
}

// 消费者
// 消费者线程在 while(1) 中持续等待并消费数据
// 消费者首先加锁 pthread_mutex_lock(&g_mutex)
// 检查链表是否为空 while(g_header == NULL)
// 如果链表为空，调用 pthread_cond_wait(&g_cond, &g_mutex) 进入等待状态
// 此时，消费者线程会解锁并挂起自己，等待生产者发出信号来唤醒它
// 当消费者线程被唤醒时，链表有数据，消费者从链表头部删除一个节点并消费其数据
// 完成消费操作后，解锁 pthread_mutex_unlock(&g_mutex)
void *thread_consumer(void *arg)
{
    // 线程分离
    pthread_detach(pthread_self());

    while (1)
    {
        pthread_mutex_lock(&g_mutex);
        while (g_header == NULL) // 多个消费者的时候用while
        {
            // 没有数据就等信号
            pthread_cond_wait(&g_cond, &g_mutex);
            // 这个函数有三个功能：
            // 1、解锁
            // 2、阻塞等待条件变量，让出CPU，不消耗CPU的时间
            // 3、等到条件变量被满足，立即加锁
        }

        // 程序到这个地方：有数据消费

        // 头删
        Node *delNode = g_header;
        g_header = g_header->next;
        printf("consumer %lu, dataId = %d\n", pthread_self(), delNode->val);

        // 释放空间
        delete delNode;

        // 解锁
        pthread_mutex_unlock(&g_mutex);
    }

    // 线程退出
    pthread_exit(NULL);
}

// main函数所属的线程就是主线程
// 主线程：初始化互斥锁和条件变量，创建生产者和消费者线程后进入无限休眠保持程序运行
int main()
{
    // 生成随机数种子
    srand(time(NULL));

    // 初始化锁
    pthread_mutex_init(&g_mutex, NULL);

    // 初始化条件变量
    pthread_cond_init(&g_cond, NULL);

    // 创建生产者线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, thread_producer, NULL);
    if (ret != 0)
    {
        perror("thread create error");
        exit(-1);
    }

    // 创建消费者线程 1
    pthread_t tid1;
    ret = pthread_create(&tid1, NULL, thread_consumer, NULL);
    if (ret != 0)
    {
        perror("thread create error");
        exit(-1);
    }

    // 创建消费者线程 2
    pthread_t tid2;
    ret = pthread_create(&tid2, NULL, thread_consumer, NULL);
    if (ret != 0)
    {
        perror("thread create error");
        exit(-1);
    }

    while (1)
    {
        sleep(1);
    }

    // 释放锁
    pthread_mutex_destroy(&g_mutex);

    // 释放条件变量
    pthread_cond_destroy(&g_cond);

    return 0;
}